package eval;

import network.Network;
import network.Port;
import network.Rule;
import networkLoader.FBNetwork;
import partition.SubspacePartition;
import verifier.JiffyVerifier;
import verifier.Ports.PersistentPorts;
import verifier.util.Changes;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.util.*;

public class ConcurrentEval {
    static Network network;

    public static void main(String[] args) {
        System.out.println("Zero Memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1000L * 1000L)) + " M");
        network = FBNetwork.getNetworkSrcHackMore();
        System.out.println("Network Memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1000L * 1000L)) + " M");
//        seq(false);
//        System.gc();
//        seq(true);
//        SS(false);
//        System.gc();
        SS(true);
//        SS(true);
//        SN(true);
//        System.gc();
//        SG(false);
//        System.gc();
//        SG(true);
//        System.gc();
//        SSSG(false);
//        System.gc();
//        SSSG(true);
    }

    public static void seq(boolean useBatch) {
        System.out.println("useBatch: " + useBatch);
        JiffyVerifier verifier = new JiffyVerifier(network, new PersistentPorts());
        long s = System.nanoTime();
        if (useBatch) {
            s = System.nanoTime();
            Changes changes = verifier.insertMiniBatch(network.getInitialRules());
            verifier.update(changes);
        } else {
            for (Rule rule : network.getInitialRules()) {
                Changes changes = verifier.insertMiniBatch(new ArrayList<>(Collections.singletonList(rule)));
                verifier.update(changes);
            }
        }
        System.out.println("$seq: " + (System.nanoTime() - s));
        System.out.println("#EC: " + verifier.predSize());
    }

    public static void SN(boolean useBatch) {
        for (int i = 0; i < 100; i++) {
            List<Rule> rulesInSS = new ArrayList<>();
            for (Rule rule : network.getInitialRules()) {
                if (rule.getMatch().longValue() >> 24 == i) { // test pod1 as subnet
                    rulesInSS.add(rule);
                }
            }
            if (rulesInSS.size() == 0) break;

            JiffyVerifier verifier = new JiffyVerifier(network, new PersistentPorts());
            long s = System.nanoTime();
            if (useBatch) {
                s = System.nanoTime();
                Changes changes = verifier.insertMiniBatch(rulesInSS);
                changes.aggrBDDs();
                verifier.update(changes);
            } else {
                for (Rule rule : rulesInSS) {
                    Changes changes = verifier.insertMiniBatch(new ArrayList<>(Collections.singletonList(rule)));
                    changes.aggrBDDs();
                    verifier.update(changes);
                }
            }
            long time = System.nanoTime() - s;
            System.out.println("====subspace " + i + "====");
            System.out.println("useBatch: " + useBatch);
            System.out.println("$SS: " + time);
            System.out.println("#rules: " + rulesInSS.size());
            System.out.println("#ECs: " + verifier.predSize());
        }

    }

    public static void SS(boolean useFFMT) {
        HashMap<Long, List<Rule>> ssToRules = new HashMap<>();
        for (Rule rule : network.getInitialRules()) {
            long ssId = (rule.getMatch().longValue() >> 16);
            ssToRules.computeIfAbsent(ssId, k -> new ArrayList<>());
            ssToRules.get(ssId).add(rule);
        }

        for (List<Rule> rules : ssToRules.values()) {
            System.gc();
            System.out.println("Total Memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / (1000L * 1000L)) + " M");
            long memoryBefore = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
            // MemoryUsage heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
            // long initMem = heapMemoryUsage.getUsed();

            SubspacePartition subspacePartition = new SubspacePartition(network, null, false);
            subspacePartition.run(rules, useFFMT);
            System.out.println("Subspace Memory: " + ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
                                                - memoryBefore) / (1000L * 1000L)) + " M");
            // heapMemoryUsage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
            // System.out.println(heapMemoryUsage.getUsed() - initMem);
            System.gc();
//            HashMap<Port, HashSet<Integer>> model = subspacePartition.getNetworkModel();
//            double s = System.nanoTime();
//            Checker.allPair(network, model);
//            System.out.println("all pair: " + ((System.nanoTime() - s) / (1000L * rules.size())) + " us per-update");
        }
    }

    public static void SG(boolean useFFMT) {
        SubspacePartition subspacePartition = new SubspacePartition(network, network.getInitialRules(), true);
        subspacePartition.run(useFFMT);
    }

    public static void SSSG(boolean useFFMT) {
        HashMap<Integer, List<Rule>> ssToRules = new HashMap<>();
        for (Rule rule : network.getInitialRules()) {
            long ssId = (rule.getMatch().longValue() >> 24);
            ssToRules.computeIfAbsent((int) (ssId), k -> new ArrayList<>());
            ssToRules.get((int) ssId).add(rule);
        }

        for (List<Rule> rules : ssToRules.values()) {
            SubspacePartition subspacePartition = new SubspacePartition(network, rules, true);
            subspacePartition.run(useFFMT);
        }
    }
}